package com.lej.core.utils.extention

import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.annotation.LayoutRes
import androidx.databinding.DataBindingUtil
import androidx.databinding.ViewDataBinding
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.ViewHolder

fun <T> RecyclerView.generate(
    res: Int,
    diffCallback: DiffUtil.ItemCallback<T>? = null,
    genViewHolder: ((View) -> BindingViewHolder)? = null,
    convert: ((helper: BindingViewHolder, item: T?) -> Unit)? = null
): BindingAdapter<T> {
    val adapter = BindingAdapter(res, diffCallback, genViewHolder, convert)
    this.adapter = adapter
    return adapter
}

fun <T> RecyclerView.generateMultiType(
    types: HashMap<Int, Int>,
    getType: (item: T) -> Int,
    diffCallback: DiffUtil.ItemCallback<T>? = null,
    convert: ((helper: BindingViewHolder, item: T?) -> Unit)? = null
): MultiTypeBindingAdapter<T> {
    val adapter = MultiTypeBindingAdapter(types, getType, diffCallback, convert)
    this.adapter = adapter
    return adapter
}

class MultiTypeBindingAdapter<T>(
    val types: HashMap<Int, Int>,
    val getType: (item: T) -> Int,
    diffCallback: DiffUtil.ItemCallback<T>? = null,
    convert: ((helper: BindingViewHolder, item: T?) -> Unit)?
) : BaseBindingAdapter<T>(diffCallback, convert) {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BindingViewHolder {
        val res = types[viewType]!!
        val view = LayoutInflater.from(parent.context)
            .inflate(res, parent, false)
        return BindingViewHolder(view)
    }

    override fun getItemViewType(position: Int): Int {
        return getType(data[position])
    }
}

class BindingAdapter<T>(
    @LayoutRes val res: Int,
    diffCallback: DiffUtil.ItemCallback<T>? = null,
    val genViewHolder: ((View) -> BindingViewHolder)? = null,
    convert: ((helper: BindingViewHolder, item: T?) -> Unit)?
) : BaseBindingAdapter<T>(diffCallback, convert) {
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BindingViewHolder {
        val view = LayoutInflater.from(parent.context)
            .inflate(res, parent, false)
        return genViewHolder?.invoke(view) ?: BindingViewHolder(view)
    }
}

abstract class BaseBindingAdapter<T>(
    val diffCallback: DiffUtil.ItemCallback<T>? = null,
    val convert: ((helper: BindingViewHolder, item: T?) -> Unit)?
) : RecyclerView.Adapter<BindingViewHolder>() {
    var data: MutableList<T> = mutableListOf()
    override fun onBindViewHolder(holder: BindingViewHolder, position: Int) {
        convert?.invoke(holder, data.getOrNull(position))
    }

    override fun getItemCount(): Int = data.count()
    fun setNewData(newList: List<T>) {
        data = newList.toMutableList()
        notifyDataSetChanged()
    }

    fun setNewDataDiff(newList: MutableList<T>) {
        val callback = object : DiffUtil.Callback() {
            override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
                val old = data[oldItemPosition]
                val new = newList[newItemPosition]
                return diffCallback?.areItemsTheSame(old, new) ?: old == new
            }

            override fun getOldListSize() = data.size

            override fun getNewListSize(): Int = newList.size

            override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
                val old = data[oldItemPosition]
                val new = newList[newItemPosition]
                return diffCallback?.areContentsTheSame(old, new) ?: old == new
            }
        }
        val diffResult = DiffUtil.calculateDiff(callback, true)
        diffResult.dispatchUpdatesTo(this)
        data = newList
    }
}

/**返回是否成功设置diff，比如之前没数据之类的会失败*/
inline fun <T> BaseBindingAdapter<T>.setNewDataDiff(
    oldList: List<T>,
    newList: List<T>,
    detectMoves: Boolean = false,
    crossinline contentSame: (oldItem: T, newItem: T) -> Boolean = { a, b -> a == b },
    /**是否类型相同 （同一个ViewHolder）*/
    crossinline itemSame: (oldItem: T, newItem: T) -> Boolean = { _, _ -> true }
): Boolean {
    val callback = object : DiffUtil.Callback() {
        override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
            val old = oldList[oldItemPosition]
            val new = newList[newItemPosition]
            return itemSame(old, new)
        }

        override fun getOldListSize() = oldList.size

        override fun getNewListSize(): Int = newList.size

        override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
            val old = oldList[oldItemPosition]
            val new = newList[newItemPosition]
            return contentSame(old, new)
        }
    }
    val diffResult = DiffUtil.calculateDiff(callback, detectMoves)
    diffResult.dispatchUpdatesTo(this)
    return true
}


open class BindingViewHolder(view: View) : ViewHolder(view) {
    @Suppress("UNCHECKED_CAST")
    fun <T : ViewDataBinding> getBind(): T? {
        return DataBindingUtil.bind<ViewDataBinding>(itemView) as? T
    }
}
